#include "rpcprovider.h"
#include "mprpcapplication.h"
#include "rpcheader.pb.h"
#include <google/protobuf/service.h>
#include "logger.h"
#include "zookeeperutil.h"

// 怎么判断缺少头文件 // pointer or reference to incomplete type "google::protobuf::ServiceDescriptor" is not allowed 这种错误一般是缺少头文件导致的

/**
 *                    serveice name => service描诉
 *                        |=》 serveice* 记录服务对象
 *                        |=》 method count 记录方法数量
 */
// 这里市框架提供给外部使用的，可以发布rpc方法的函数接口
void RpcProvider::NotifyService(google::protobuf::Service *service)
{
    ServiceInfo service_info;
    // 发布这个方法，给别人远程调用 获取Userservice方法
    const google::protobuf::ServiceDescriptor *pserviceDesc = service->GetDescriptor();

    // 获取服务的名字
    std::string service_name = pserviceDesc->name();
    // std::cout << service_name << std::endl;
    LOG_INFO("service_name:%s", service_name.c_str());

    // 获取服务对象service的方法数量
    int methodCnt = pserviceDesc->method_count();

    for (int i = 0; i < methodCnt; i++)
    {
        // 获取了服务对象指定下标的服务方法描诉（抽象描诉）
        const google::protobuf::MethodDescriptor *pmethod = pserviceDesc->method(i);
        std::string method_name = pmethod->name();
        //  std::cout << method_name << std::endl;
        LOG_INFO("method_name:%s", method_name.c_str());
        service_info.m_methodMap.insert({method_name, pmethod});
    }

    service_info.m_service = service;
    m_serviceMap.insert({service_name, service_info});
}
// 启动rpc服务节点，开始提供rpc远程调用服务
void RpcProvider::Run()
{
    std::string ip = MprpcApplication::GetInstance()->GetConfig().Load("rpcserverip");
    uint16_t port = atoi(MprpcApplication::GetInstance()->GetConfig().Load("rpcserverport").c_str());
    muduo::net::InetAddress address(ip, port);

    // 创建rpc服务端，并绑定事件循环
    muduo::net::TcpServer server(&m_eventLoop, address, "RpcProvider");
    // 绑定回调 连接回调 和消息独写回调
    server.setConnectionCallback(std::bind(&RpcProvider::OnConnection, this, std::placeholders::_1));
    server.setMessageCallback(std::bind(&RpcProvider::OnMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    // 设置线程的数量
    server.setThreadNum(4);
    ZkClient zkcli;
    zkcli.Start(); // 连接zkserver

//  信息保存到zkserver 名字为ip/port
    for(auto& sp:m_serviceMap)
    {
        std::string service_path="/"+sp.first;
        zkcli.Create(service_path.c_str(),nullptr,0);
        for(auto& mp:sp.second.m_methodMap)
        {
            // service_name/method_name
            std::string method_path = service_path+"/"+mp.first;
            char method_path_data[128]={0};
            sprintf(method_path_data,"%s:%d",ip.c_str(),port);
            // ZOO_EPHEMERAL 临时性节点
            zkcli.Create(method_path.c_str(),method_path_data,strlen(method_path_data),ZOO_EPHEMERAL);
        }
    }

    
    std::cout << "RpcProvider start ip:" << ip << " port:" << port << std::endl;
    // 启动网络服务
    server.start();
    m_eventLoop.loop();
}

void RpcProvider::OnConnection(const muduo::net::TcpConnectionPtr &conn)
{
    if (!conn->connected())
    {
        // 和 rpc client的连接断开了
        std::cout << "RpcProvider OnConnection connected" << std::endl;
        conn->shutdown();
    }
}
/*
在框架内部，RpcProvider 和RpcConsumer 协商好protobuf的数据类型
service——name method——name  args

//  userserviceloginzhangsan  这样肯定不行
    定义proto的message类型，进行数据的序列化和反序列化
    head_size(4个字节) header_Str
    std::string insert copy方法
*/

void RpcProvider::OnMessage(const muduo::net::TcpConnectionPtr &conn,
                            muduo::net::Buffer *buffer,
                            muduo::Timestamp receiveTime)
{
    // 建立连接的读写数据
    // 网络上接受远程rpc调用请求的字符流 Login args
    std::string recv_buf = buffer->retrieveAllAsString();

    // 从字符流中读取前4个字节的内容
    uint32_t header_size = 0;
    recv_buf.copy((char *)&header_size, 4, 0);

    // 根据headersize读取原始字符流
    std::string rpc_header_str = recv_buf.substr(4, header_size);

    // 反序列化数据
    mprpc::RpcHeader rpc_header;
    std::string service_name;
    std::string method_name;
    uint32_t args_size;
    if (rpc_header.ParseFromString(rpc_header_str))
    {
        // 数据反序列成功
        // 取出数据
        service_name = rpc_header.service_name();
        method_name = rpc_header.method_name();
        args_size = rpc_header.args_size();
    }
    else
    {
        // 数据反序列化失败
        std::cout << "rpc_header_str" << rpc_header_str << "parsr error" << std::endl;
        return;
    }
    std::string arg_str = recv_buf.substr(header_size + 4, args_size);
    // 打印调试信息
    std::cout << "=========================================================================" << std::endl;
    std::cout << "header_size:" << header_size << std::endl;
    std::cout << "rpc_header_str:" << rpc_header_str << std::endl;
    std::cout << "service_name:" << service_name << std::endl;
    std::cout << "method_name:" << method_name << std::endl;
    // std::cout << "args_size:" << args_size << std::endl;
    std::cout << "arg_str:" << arg_str << std::endl;
    std::cout << "=========================================================================" << std::endl;

    // 获取service对象和method对象
    auto it = m_serviceMap.find(service_name);
    if (it == m_serviceMap.end())
    {
        // 没找到
        std::cout << "is not exit" << std::endl;
        return;
    }

    auto mit = it->second.m_methodMap.find(method_name);

    if (mit == it->second.m_methodMap.end())
    {
        // 没找到方法
        std::cout << service_name << ":" << method_name << " is not exit" << std::endl;
        return;
    }
    // 找到了
    google::protobuf::Service *service = it->second.m_service;      // 获取service 对象 new  Userservice
    const google::protobuf::MethodDescriptor *method = mit->second; // 获取method对象 Login

    // 调用方法 生成rpc方法请求得request 和 response
 
    google::protobuf::Message *request = service->GetRequestPrototype(method).New();
    if (!request->ParseFromString(arg_str))
    {
        std::cout << " request ParseFromString error" << arg_str << std::endl;
        return;
    }
    // 这句代码不懂TODO
    google::protobuf::Message *response = service->GetResponsePrototype(method).New(); // 是这样的，因为我们之前的User,proto是在业务层写的，我们这里得到的argstr是字符串撒，要反序列化成那种格式，
    // 我们能发现所有定义的类都是google::protobuf::Message 类型 ，然后通过这个方法，就可以去调用找到哪个类型（通过我们传入的参数的名字）
        // void Login(::google::protobuf::RpcController *controller,                                   找到这个request
        //        const ::fixbug::LoginRequest *request,
        //        ::fixbug::LoginResponse *response,
        //        ::google::protobuf::Closure *done)

    // 给下面的饿method方法得调用，绑定一个Clossure回调函数
    google::protobuf::Closure *done = google::protobuf::NewCallback<RpcProvider, const muduo::net::TcpConnectionPtr &, google::protobuf::Message *>(this, &RpcProvider::SendRpcResponse, conn, response);

    // 在框架上根据远端rpc请求，调用当前rpc节点上发布得方法
    // new Userservice().Login(CONTORLLER REQUEST RESPONSE DONE);
    service->CallMethod(method, NULL, request, response, done);
}

void RpcProvider::SendRpcResponse(const muduo::net::TcpConnectionPtr &conn,
                                  google::protobuf::Message *response)
{
    std::string response_str;
    if (response->SerializeToString(&response_str))
    {
        // 序列化成功
        conn->send(response_str);
    }
    else
    {
        std::cout << "response SerializeToString error" << std::endl;
    }
    conn->shutdown(); // 模拟http短链接服务 ，由rpcprovider主动断开连接
}
